package org.apache.lucene.search;

import java.io.IOException;

import org.apache.lucene.index.IndexReader;
import org.apache.solr.core.SolrCore;
import org.apache.solr.schema.Xscorer;

public class B2BTopScoreDocCollector extends TopDocsCollector<ScoreDoc> {
	ScoreDoc pqTop;
	int docBase = 0;
	Scorer scorer;
	IndexReader reader;
	private Xscorer xscorer;

	// prevents instantiation
	private B2BTopScoreDocCollector(int numHits, SolrCore core) {
		super(new HitQueue(numHits, true));
		// HitQueue implements getSentinelObject to return a ScoreDoc, so we
		// know
		// that at this point top() is already initialized.
		pqTop = pq.top();
		this.xscorer = core.getSchema().getXscorer();

	}

	@Override
	public void collect(int doc) throws IOException {
		float score = scorer.score();

		// This collector cannot handle these scores:
		assert score != Float.NEGATIVE_INFINITY;
		assert !Float.isNaN(score);
		
		score = xscorer.score(doc, score);
		totalHits++;
		if (score <= pqTop.score) {
			// Since docs are returned in-order (i.e., increasing doc Id), a
			// document
			// with equal score to pqTop.score cannot compete since HitQueue
			// favors
			// documents with lower doc Ids. Therefore reject those docs
			// too.
			return;
		}
		pqTop.doc = doc + docBase;
		pqTop.score = score;
		pqTop = pq.updateTop();
	}

	@Override
	public boolean acceptsDocsOutOfOrder() {
		return false;
	}

	/**
	 * Creates a new {@link TopScoreDocCollector} given the number of hits to
	 * collect and whether documents are scored in order by the input
	 * {@link Scorer} to {@link #setScorer(Scorer)}.
	 * 
	 * <p>
	 * <b>NOTE</b>: The instances returned by this method pre-allocate a full
	 * array of length <code>numHits</code>, and fill the array with sentinel
	 * objects.
	 * 
	 * @param cor
	 */
	public static B2BTopScoreDocCollector create(int numHits,
			boolean docsScoredInOrder, SolrCore core) {

		if (numHits <= 0) {
			throw new IllegalArgumentException(
					"numHits must be > 0; please use TotalHitCountCollector if you just need the total hit count");
		}
		B2BTopScoreDocCollector collector = new B2BTopScoreDocCollector(
				numHits, core);
		return collector;

	}

	@Override
	protected TopDocs newTopDocs(ScoreDoc[] results, int start) {
		if (results == null) {
			return EMPTY_TOPDOCS;
		}

		// We need to compute maxScore in order to set it in TopDocs. If start
		// == 0,
		// it means the largest element is already in results, use its score as
		// maxScore. Otherwise pop everything else, until the largest element is
		// extracted and use its score as maxScore.
		float maxScore = Float.NaN;
		if (start == 0) {
			maxScore = results[0].score;
		} else {
			for (int i = pq.size(); i > 1; i--) {
				pq.pop();
			}
			maxScore = pq.pop().score;
		}

		return new TopDocs(totalHits, results, maxScore);
	}

	@Override
	public void setNextReader(IndexReader reader, int base) throws IOException {
		this.reader = reader;
		docBase = base;
		xscorer.setNextReader(reader, base);
	}

	@Override
	public void setScorer(Scorer scorer) throws IOException {
		this.scorer = scorer;
	}
}
